package vn.edu.saigontech.ncms.web.receipt;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import vn.edu.saigontech.ncms.model.enumeration.Currency;
import vn.edu.saigontech.ncms.model.info.AbstractReceipt;
import vn.edu.saigontech.ncms.model.info.MultiTimeReceipt;
import vn.edu.saigontech.ncms.model.info.Offering;
import vn.edu.saigontech.ncms.model.info.Receipt;
import vn.edu.saigontech.ncms.model.info.ReceiptInfo;
import vn.edu.saigontech.ncms.model.info.ReceiptMaintenance;
import vn.edu.saigontech.ncms.model.info.Student;
import vn.edu.saigontech.ncms.model.program.Level;
import vn.edu.saigontech.ncms.model.system.Money;
import vn.edu.saigontech.ncms.model.system.ReceiptAbstractDiscountItem;
import vn.edu.saigontech.ncms.model.system.ReceiptPercentDiscountItem;
import vn.edu.saigontech.ncms.model.system.USD;
import vn.edu.saigontech.ncms.model.user.User;
import vn.edu.saigontech.ncms.service.LevelService;
import vn.edu.saigontech.ncms.service.MultiTimeReceiptService;
import vn.edu.saigontech.ncms.service.OfferingMemberService;
import vn.edu.saigontech.ncms.service.OfferingService;
import vn.edu.saigontech.ncms.service.ProgramService;
import vn.edu.saigontech.ncms.service.ReceiptAbstractDiscountItemService;
import vn.edu.saigontech.ncms.service.ReceiptAmountDiscountItemService;
import vn.edu.saigontech.ncms.service.ReceiptInfoService;
import vn.edu.saigontech.ncms.service.ReceiptMaintenanceService;
import vn.edu.saigontech.ncms.service.ReceiptPercentDiscountItemService;
import vn.edu.saigontech.ncms.service.ReceiptRefundService;
import vn.edu.saigontech.ncms.service.ReceiptService;
import vn.edu.saigontech.ncms.service.StudentService;
import vn.edu.saigontech.ncms.service.USDService;
import vn.edu.saigontech.ncms.service.UserService;
import vn.edu.saigontech.ncms.util.Checker;
import vn.edu.saigontech.ncms.util.DateUtil;
import vn.edu.saigontech.ncms.util.NumberUtil;
import vn.edu.saigontech.ncms.vo.SelectOption;
import vn.edu.saigontech.ncms.web.BaseAction;
import flexjson.JSONSerializer;

@SuppressWarnings("serial")
public class OfferingReceiptAction extends BaseAction {
	
	private Long id;
	private Long levelId;
	private String number;
	private String date;
	private String fromDate;
	private String toDate;
	private Long offeringId;
	private String offeringName;
	private Long studentId;
	private String studentFirstName;
	private String studentLastName;
	private String studentDateOfBirth;
	private Long discountItemId;
	private Long discountItemId2;
	private Long maintenanceId;
	private Double fee;
	private boolean active;
	private boolean newStudentOnly;
	private Long cashierId;
	private String comment;
	
	private String type;
	private String expiredDate;
	private Double totalFee;
	private Double realFee;
	
	public String showAdd() {
		Level level = LevelService.INSTANCE.findById(this.levelId);
		USD usd = USDService.INSTANCE.findCurrent();
		if (getType() == null) {
			type = "one";
		}
		if(level != null) {
			double tuition = (usd.getAmount() * level.getTuition().getAmount());
			double sessions = level.getHours() / 1.5;
			double feePerSession = usd.getAmount() * level.getTuition().getAmount() / sessions;
			REQUEST().setAttribute("feePerSession", NumberUtil.round(feePerSession, 1));			
			REQUEST().setAttribute("tuition", NumberUtil.round(tuition, 3));
			REQUEST().setAttribute("level", level);
			REQUEST().setAttribute("sessions", sessions);
			
			Offering offering = OfferingService.INSTANCE.findById(this.offeringId);

			if(offering != null && this.type.equals("multi")) {
				Date startDate = offering.getStartDate();
				Date endDate = offering.getEndDate();
				int duration = (int) DateUtil.getDifferenceDate(endDate, startDate);
				Date expiredDate = DateUtil.addDay(startDate, duration / 2);
				
				REQUEST().setAttribute("expiredDate", expiredDate);
				REQUEST().setAttribute("offeringId", offeringId);
				REQUEST().setAttribute("submittedFee", NumberUtil.round(tuition / 2, 3));
			}
		}
		
		Map<String, List<ReceiptPercentDiscountItem>> discountItems = ReceiptPercentDiscountItemService.INSTANCE.findAllAndSeparate();
		List<String> keys = new ArrayList<String>(discountItems.keySet());
		Collections.sort(keys, new Comparator<String> () {
			@Override
			public int compare(String s1, String s2) {
				s1 = s1.substring(0, s1.length() - 1);
				s2 = s2.substring(0, s2.length() - 1);
				int v1 = Integer.parseInt(s1);
				int v2 = Integer.parseInt(s2);
				return v1 - v2;
			}
		});
		Set<Offering> offerings = (level != null)? level.getOfferings() : new HashSet<Offering>();
		Set<Offering> ret = new HashSet<Offering>();
		Date date = new Date();
		for(Offering offering : offerings) {
			if(!offering.getEndDate().before(date))
				ret.add(offering);
		}
		REQUEST().setAttribute("offerings", ret);
		//REQUEST().setAttribute("programs", ProgramService.INSTANCE.findAllAreActive());
		REQUEST().setAttribute("programs", ProgramService.INSTANCE.findAllAreOpening());
		REQUEST().setAttribute("student", StudentService.INSTANCE.findById(this.studentId));		
		REQUEST().setAttribute("discountItems", discountItems);
		REQUEST().setAttribute("maintenances", ReceiptMaintenanceService.INSTANCE.findByStudentId(this.studentId));
		REQUEST().setAttribute("amountDiscountItems", ReceiptAmountDiscountItemService.INSTANCE.findAll());
		REQUEST().setAttribute("maintenances", ReceiptMaintenanceService.INSTANCE.findByStudentId(this.studentId));
		REQUEST().setAttribute("keys", keys);
		REQUEST().setAttribute("cashiers", UserService.INSTANCE.findByRole("Consulting"));
		REQUEST().setAttribute("amount", usd.getAmount());
		REQUEST().setAttribute("date", usd.getDate());
		return SUCCESS;
	}
	
	public String add() {
		Student student = StudentService.INSTANCE.findById(studentId);
		Offering offering = OfferingService.INSTANCE.findById(offeringId);
		ReceiptAbstractDiscountItem discountItem = ReceiptAbstractDiscountItemService.INSTANCE.findById(this.discountItemId);
		ReceiptAbstractDiscountItem discountItem2 = ReceiptAbstractDiscountItemService.INSTANCE.findById(this.discountItemId2);
		ReceiptMaintenance maintenance = updateMaintenace(this.maintenanceId, this.realFee);
		User cashier = UserService.INSTANCE.findById(cashierId);
		Date date = DateUtil.fromStringWithTime(this.date, this.i18n("format.datetime"));
		String notation = offering.getLevel().getProgram().getNotation();
		String receiptNotation = "";
		if(discountItem != null) {
			receiptNotation = discountItem.getReceiptNotation();
		}
		String number = ReceiptInfoService.INSTANCE.generateReceiptInfoName(ReceiptInfo.HP, notation, receiptNotation, DateUtil.getYear(date));
		
		AbstractReceipt entity = null;
		if (this.type.equals("multi")) {
			MultiTimeReceipt offeringReceipt = new MultiTimeReceipt();
			offeringReceipt.setTotalFee(new Money(this.totalFee, Currency.VND));
			offeringReceipt.setSubmittedTime(1);
			Date edate = DateUtil.fromStringWithTime(this.expiredDate, this.i18n("format.datetime"));
			offeringReceipt.setExpiredDate(edate);
			
			offeringReceipt.setNumber(number);
			offeringReceipt.setStudent(student);
			offeringReceipt.setOffering(offering);
			offeringReceipt.setDate(date);
			offeringReceipt.setDiscountItem(discountItem);
			offeringReceipt.setDiscountItem2(discountItem2);
			offeringReceipt.setReceiptMaintenance(maintenance);
			offeringReceipt.setFee(new Money(this.fee, Currency.VND));
			offeringReceipt.setActive(true);
			offeringReceipt.setCashier(cashier);
			offeringReceipt.setComment(comment);
			
			entity = MultiTimeReceiptService.INSTANCE.persist(offeringReceipt);
		}
		else {
			Receipt offeringReceipt = new Receipt();
			offeringReceipt.setNumber(number);
			offeringReceipt.setStudent(student);
			offeringReceipt.setOffering(offering);
			offeringReceipt.setDate(date);
			offeringReceipt.setDiscountItem(discountItem);
			offeringReceipt.setDiscountItem2(discountItem2);
			offeringReceipt.setReceiptMaintenance(maintenance);
			offeringReceipt.setFee(new Money(this.fee, Currency.VND));
			offeringReceipt.setActive(true);
			offeringReceipt.setCashier(cashier);
			offeringReceipt.setComment(comment);
			
			entity = ReceiptService.INSTANCE.persist(offeringReceipt);
		}
		
		if(Checker.isNotNull(entity)) {
			this.addLoggingDetail(BaseAction.ADD, "logging.receipt.add", student.getFullName(), entity.getNumber(), offering.getName());
			OfferingMemberService.INSTANCE.add(entity.getId());
		}
		
		this.id = entity.getId();
		return SUCCESS;
	}
	
	private ReceiptMaintenance updateMaintenace(Long id, Double fee) {
		ReceiptMaintenance maintenance = ReceiptMaintenanceService.INSTANCE.findById(id);
		if (maintenance == null) return null;
		if (fee > maintenance.getFee()) maintenance.setActive(false);
		else maintenance.setFee(maintenance.getFee() - fee);
		ReceiptMaintenance updatedMaintenace = ReceiptMaintenanceService.INSTANCE.persist(maintenance);
		return updatedMaintenace;
	}

	public String info() {
		REQUEST().setAttribute("maintenance", ReceiptMaintenanceService.INSTANCE.findByReceiptId(this.id));
		REQUEST().setAttribute("refund", ReceiptRefundService.INSTANCE.findByReceiptId(this.id));
		REQUEST().setAttribute("receipt", ReceiptService.INSTANCE.findById(this.id));
		Map<String, List<ReceiptPercentDiscountItem>> discountItems = ReceiptPercentDiscountItemService.INSTANCE.findAllAndSeparate();
		List<String> keys = new ArrayList<String>(discountItems.keySet());
		Collections.sort(keys, new Comparator<String> () {
			@Override
			public int compare(String s1, String s2) {
				s1 = s1.substring(0, s1.length() - 1);
				s2 = s2.substring(0, s2.length() - 1);
				int v1 = Integer.parseInt(s1);
				int v2 = Integer.parseInt(s2);
				return v1 - v2;
			}
		});
		REQUEST().setAttribute("discountItems", discountItems);
		REQUEST().setAttribute("amountDiscountItems", ReceiptAmountDiscountItemService.INSTANCE.findAll());
		REQUEST().setAttribute("keys", keys);
		return SUCCESS;
	}
	
	public String showEdit() {
		REQUEST().setAttribute("programs", ProgramService.INSTANCE.findAllAreActive());
		REQUEST().setAttribute("receipt", ReceiptService.INSTANCE.findById(this.id));
		REQUEST().setAttribute("cashiers", UserService.INSTANCE.findAllByRole("Consulting"));
		ReceiptPercentDiscountItemService.INSTANCE.findAllAndSeparate();
		Map<String, List<ReceiptPercentDiscountItem>> discountItems = ReceiptPercentDiscountItemService.INSTANCE.findAllAndSeparate();
		List<String> keys = new ArrayList<String>(discountItems.keySet());
		Collections.sort(keys, new Comparator<String> () {
			@Override
			public int compare(String s1, String s2) {
				s1 = s1.substring(0, s1.length() - 1);
				s2 = s2.substring(0, s2.length() - 1);
				int v1 = Integer.parseInt(s1);
				int v2 = Integer.parseInt(s2);
				return v1 - v2;
			}
		});
		REQUEST().setAttribute("discountItems", discountItems);
		REQUEST().setAttribute("amountDiscountItems", ReceiptAmountDiscountItemService.INSTANCE.findAllAreActive());
		REQUEST().setAttribute("keys", keys);

		return SUCCESS;
	}
	
	public String edit() {
		Receipt receipt = ReceiptService.INSTANCE.findById(this.id);
		Offering offering = OfferingService.INSTANCE.findById(offeringId);
		receipt.setOffering(offering);
		receipt.setDate(DateUtil.fromStringWithTime(this.date, this.i18n("format.datetime")));
		receipt.setFee(new Money(this.fee, Currency.VND));
		receipt.setCashier(UserService.INSTANCE.findById(cashierId));
		receipt.setActive(active);
		receipt.getOfferingMember().setActive(active);
		receipt.setComment(comment);
		
		Receipt entity = ReceiptService.INSTANCE.persist(receipt);

		if(Checker.isNotNull(entity)) {
			this.addLoggingDetail(BaseAction.EDIT, "logging.receipt.edit", entity.getStudent().getFullName(), entity.getNumber(), offering.getName());
		}
		
		return SUCCESS;
	}
	
	public String delete() {
		Receipt receipt = ReceiptService.INSTANCE.findById(this.id);
		String studentName = receipt.getStudent().getFullName();
		String number = receipt.getNumber();
		String offeringName = receipt.getOffering().getName();		
		if(ReceiptService.INSTANCE.deleteById(this.id)) {
			this.setKeyMessage("confirm.deleteSuccessfully", "receipt");
			this.addLoggingDetail(BaseAction.DELETE, "logging.receipt.delete", studentName, number, offeringName);
		}
		else {
			this.setKeyMessage("confirm.deleteUnsuccessfully", "receipt");
		}
		return SUCCESS;
	}
	
	public String showSearch() {
		REQUEST().setAttribute("cashiers", UserService.INSTANCE.findAllByRole("Managing"));
		return SUCCESS;
	}
	
	public String search() {
		Date birthday = Checker.isDate(this.studentDateOfBirth, this.i18n("format.date"))? DateUtil.fromString(this.studentDateOfBirth, this.i18n("format.date")) : null;
		Date date = Checker.isDate(this.date, this.i18n("format.date"))? DateUtil.fromString(this.date, this.i18n("format.date")) : null;
		Date fromDate = Checker.isDate(this.fromDate, this.i18n("format.date"))? DateUtil.fromString(this.fromDate, this.i18n("format.date")) : null;
		Date toDate = Checker.isDate(this.toDate, this.i18n("format.date"))? DateUtil.fromString(this.toDate, this.i18n("format.date")) : null;
		if (Checker.isNotNull(toDate)) toDate = DateUtil.endOfDate(toDate); 
		Money money = Checker.isNotNull(this.fee)? new Money(this.fee, Currency.VND) : null;
		
		Collection<Receipt> receipts = ReceiptService.INSTANCE.find(
				this.studentFirstName, this.studentLastName, birthday,
				this.number, date, fromDate, toDate, this.offeringName, money,
				this.cashierId,	this.comment);
		List<Receipt> ret = new ArrayList<Receipt>();
		if(newStudentOnly) {
			for(Receipt receipt : receipts) {
				int count = receipt.getStudent().getOfferingReceipts().size();
				if(count == 1)
					ret.add(receipt);
			}
		} else
			ret.addAll(receipts);
		
		REQUEST().setAttribute("receipts", ret);
		this.addLoggingDetail(BaseAction.SEARCH, "logging.receipt.search");
		return SUCCESS;
	}
	
	public String requestOfferingByLevelId() {
		Level level = LevelService.INSTANCE.findById(this.levelId);
		List<SelectOption> optionList = new ArrayList<SelectOption>();
		for(Offering offering : level.getOfferings()) {
			SelectOption selectOption = new SelectOption();
			selectOption.setValue(String.valueOf(offering.getId()));
			selectOption.setCaption(DateUtil.toString(offering.getStartDate(), this.i18n("format.date")) +  "-" +
					                DateUtil.toString(offering.getEndDate(), this.i18n("format.date")) + " # " +  
					                offering.getName());
			optionList.add(selectOption);
		}
		try {
			RESPONSE().getWriter().write(new JSONSerializer().serialize(optionList));
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return NONE;
	}
	
	public String showAllRecently() {
		REQUEST().setAttribute("receipts", ReceiptService.INSTANCE.findAllRecently());
		return SUCCESS;
	}
	
	public String print() {
		Receipt receipt = ReceiptService.INSTANCE.findById(this.id);
		REQUEST().setAttribute("receipt", receipt);
		return SUCCESS;
	}

	public Long getLevelId() {
		return levelId;
	}

	public void setLevelId(Long levelId) {
		this.levelId = levelId;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}

	public Long getOfferingId() {
		return offeringId;
	}

	public void setOfferingId(Long offeringId) {
		this.offeringId = offeringId;
	}

	public String getOfferingName() {
		return offeringName;
	}

	public void setOfferingName(String offeringName) {
		this.offeringName = offeringName;
	}

	public Long getStudentId() {
		return studentId;
	}

	public void setStudentId(Long studentId) {
		this.studentId = studentId;
	}

	public String getStudentFirstName() {
		return studentFirstName;
	}

	public void setStudentFirstName(String studentFirstName) {
		this.studentFirstName = studentFirstName;
	}

	public String getStudentLastName() {
		return studentLastName;
	}

	public void setStudentLastName(String studentLastName) {
		this.studentLastName = studentLastName;
	}

	public String getStudentDateOfBirth() {
		return studentDateOfBirth;
	}

	public void setStudentDateOfBirth(String studentDateOfBirth) {
		this.studentDateOfBirth = studentDateOfBirth;
	}

	public Double getFee() {
		return fee;
	}

	public void setFee(Double fee) {
		this.fee = fee;
	}

	public Double getRealFee() {
		return realFee;
	}

	public void setRealFee(Double realFee) {
		this.realFee = realFee;
	}

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public Long getCashierId() {
		return cashierId;
	}

	public void setCashierId(Long cashierId) {
		this.cashierId = cashierId;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public String getFromDate() {
		return fromDate;
	}

	public void setFromDate(String fromDate) {
		this.fromDate = fromDate;
	}

	public String getToDate() {
		return toDate;
	}

	public void setToDate(String toDate) {
		this.toDate = toDate;
	}

	public Long getDiscountItemId() {
		return discountItemId;
	}

	public void setDiscountItemId(Long discountItemId) {
		this.discountItemId = discountItemId;
	}

	public boolean isNewStudentOnly() {
		return newStudentOnly;
	}

	public void setNewStudentOnly(boolean newStudentOnly) {
		this.newStudentOnly = newStudentOnly;
	}

	public Long getMaintenanceId() {
		return maintenanceId;
	}

	public void setMaintenanceId(Long maintenanceId) {
		this.maintenanceId = maintenanceId;
	}

	public String getExpiredDate() {
		return expiredDate;
	}

	public void setExpiredDate(String expiredDate) {
		this.expiredDate = expiredDate;
	}

	public Double getTotalFee() {
		return totalFee;
	}

	public void setTotalFee(Double totalFee) {
		this.totalFee = totalFee;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public Long getDiscountItemId2() {
		return discountItemId2;
	}

	public void setDiscountItemId2(Long discountItemId2) {
		this.discountItemId2 = discountItemId2;
	}
}